package br.com.ac.fatureweb.utilitary;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TemporalType;

/**
 * 
 * 
 *
 * @param <Entity>
 */
@SuppressWarnings("all")
public abstract class NgcDefaultBean<Entity> {

	private EntityManager em;

	private Class<Entity> entityClass;

	private final static String ALIAS_PADRAO_FROM = "beanT";

	private static final String AND = "AND";

	private static final String VIRGULA = ",";

	/**
	 * 
	 */
	@PostConstruct
	private void initialize() {
		entityClass = ((Class) ((java.lang.reflect.ParameterizedType) this
				.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
	}

	/**
	 * 
	 * @param entity
	 * @return
	 */
	public Entity persist(Entity entity) {
		em.persist(entity);

		Entity beanCriado = entity;

		return beanCriado;
	}

	/**
	 * 
	 * @param entity
	 * @return
	 */
	public Entity merge(Entity entity) {
		return em.merge(entity);
	}

	/**
	 * 
	 * @param entity
	 */
	public void remove(Entity entity) {
		em.remove(entity);
	}

	/**
	 * 
	 * @param id
	 */
	public void remove(Long id) {
		Entity bean = em.getReference(entityClass, id);

		em.remove(bean);
	}

	/**
	 * 
	 * @param id
	 * @return
	 */
	public Entity getEntity(Long id) {
		Entity bean = em.getReference(entityClass, id);

		return em.find(entityClass, bean);
	}

	/**
	 * 
	 * @param mapParam
	 * @param qtdSearch
	 * @param first
	 * @param mapOrder
	 * @param selectComFrom
	 * @param aliasWhere
	 * @return
	 */
	public List<Entity> search(Map<String, Object> mapParam, int qtdSearch,
			int first, Map<String, Order> mapOrder, String selectComFrom,
			String aliasWhere) {
		if (aliasWhere == null) {
			aliasWhere = ALIAS_PADRAO_FROM;
		}

		StringBuilder jpql = gerarFrom(selectComFrom, aliasWhere, false);

		Map<String, Object> mapWhere = buildWhere(mapParam, jpql, aliasWhere);

		buildOrderBy(mapOrder, jpql, aliasWhere);

		Query query = em.createQuery(jpql.toString());

		setParameterWhere(mapWhere, query);

		query.setFirstResult(first);

		query.setMaxResults(qtdSearch);

		return query.getResultList();
	}

	/**
	 * 
	 * @param mapParam
	 * @param mapOrder
	 * @param selectWithFrom
	 * @param aliasWhere
	 * @return
	 */
	public Integer count(Map<String, Object> mapParam,
			Map<String, Order> mapOrder, String selectWithFrom,
			String aliasWhere) {
		if (aliasWhere == null) {
			aliasWhere = ALIAS_PADRAO_FROM;
		}

		StringBuilder jpql = gerarFrom(selectWithFrom, aliasWhere, true);

		Map<String, Object> mapWhere = buildWhere(mapParam, jpql, aliasWhere);

		// buildOrderBy(mapOrder, jpql, aliasWhere);

		Query query = em.createQuery(jpql.toString());

		setParameterWhere(mapWhere, query);

		query.setFirstResult(0);

		query.setMaxResults(1);

		final List<Number> resultList = query.getResultList();

		return resultList != null && !resultList.isEmpty() ? resultList
				.iterator().next().intValue() : 0;
	}

	/**
	 * 
	 * @param mapParam
	 * @param first
	 * @param qtdSearch
	 * @param mapOrder
	 * @return
	 */
	public List<Entity> search(Map<String, Object> mapParam, int first,
			int qtdSearch, Map<String, Order> mapOrder) {
		return search(mapParam, first, qtdSearch, mapOrder, null, null);
	}

	/**
	 * 
	 * @param mapParam
	 * @param mapOrder
	 * @return
	 */
	public Integer count(Map<String, Object> mapParam,
			Map<String, Order> mapOrder) {
		return count(mapParam, mapOrder, null, null);
	}

	/**
	 * 
	 * @param mapOrder
	 * @param jpQl
	 * @param aliasWhere
	 */
	protected void buildOrderBy(Map<String, Order> mapOrder,
			StringBuilder jpQl, String aliasWhere) {
		if (mapOrder != null && !mapOrder.isEmpty()) {

			jpQl.append(" ORDER BY ");

			for (String campo : mapOrder.keySet()) {
				Order Order = mapOrder.get(campo);

				jpQl.append(" " + aliasWhere + "." + campo + " "
						+ Order.getOrdem() + VIRGULA);
			}

			// Removar a última virgula.
			removeLastWord(jpQl, VIRGULA);
		}
	}

	/**
	 * 
	 * @param mapParam
	 * @param jpql
	 * @param aliasWhere
	 * @return
	 */
	protected Map<String, Object> buildWhere(Map<String, Object> mapParam,
			StringBuilder jpql, String aliasWhere) {
		if (mapParam != null && !mapParam.isEmpty()) {
			final Map<String, Object> mapRetorno = new HashMap<String, Object>();

			jpql.append(" WHERE ");

			int contadorCampo = 1;

			for (String nomeCampo : mapParam.keySet()) {
				Object valorCampo = mapParam.get(nomeCampo);

				final String nomePametro = "parametro" + contadorCampo;

				final Class<?> objClass = valorCampo.getClass();

				if (objClass.isAssignableFrom(String.class)) {
					jpql.append(aliasWhere + "." + nomeCampo + " LIKE :"
							+ nomePametro);
					mapRetorno.put(nomePametro, valorCampo + "%");
				} else {
					jpql.append(aliasWhere + "." + nomeCampo + " = :"
							+ nomePametro);
					mapRetorno.put(nomePametro, valorCampo);
				}

				contadorCampo++;
			}

			removeLastWord(jpql, AND);

			return mapRetorno;
		}

		return null;
	}

	/**
	 * 
	 * @param selectWithFrom
	 * @param aliasWhere
	 * @param isCount
	 * @return
	 */
	private StringBuilder gerarFrom(String selectWithFrom, String aliasWhere,
			boolean isCount) {
		StringBuilder jpql = new StringBuilder("SELECT ");

		if (isCount) {
			jpql.append(" COUNT( " + aliasWhere + ") FROM "
					+ entityClass.getSimpleName() + " " + aliasWhere + " ");
		} else {
			jpql.append(aliasWhere + " FROM " + entityClass.getSimpleName()
					+ " " + aliasWhere + " ");

			if (selectWithFrom != null) {
				jpql = new StringBuilder(selectWithFrom + " " + aliasWhere
						+ " ");
			}
		}
		return jpql;
	}

	/**
	 * 
	 * @param jpql
	 * @param pesquisa
	 */
	private void removeLastWord(StringBuilder jpql, String pesquisa) {
		// Removar a último "AND".
		final int lastIndexOf = jpql.lastIndexOf(pesquisa);

		if (lastIndexOf > -1) {
			jpql.delete(lastIndexOf, jpql.length());
		}
	}

	/**
	 * 
	 * @param mapWhere
	 * @param query
	 */
	public static void setParameterWhere(final Map<String, Object> mapWhere,
			final Query query) {
		if (mapWhere != null && !mapWhere.isEmpty()) {
			final Set<String> lstParametros = mapWhere.keySet();

			if (lstParametros != null && !lstParametros.isEmpty()) {
				for (final String parametro : lstParametros) {
					final Object valorKey = mapWhere.get(parametro);

					if (valorKey.getClass().equals(Date.class)) {
						query.setParameter(parametro, (Date) valorKey,
								TemporalType.DATE);
					} else {
						query.setParameter(parametro, valorKey);
					}
				}
			}
		}
	}

	/**
	 * 
	 * @return
	 */
	public EntityManager getEm() {
		return em;
	}

	/**
	 * 
	 * @param em
	 */
	public void setEm(EntityManager em) {
		this.em = em;
	}

}